/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is Forte for Java, Community Edition. The Initial
* Developer of the Original Code is Sun Microsystems, Inc. Portions
* Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
*/
package org.openide.compiler;
import java.util.*;
import java.io.IOException;
import org.openide.TopManager;
import org.openide.cookies.CompilerCookie;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileSystemCapability;
import org.openide.filesystems.FileLock;
import org.openide.filesystems.FileStateInvalidException;
import org.openide.filesystems.FileSystem;
import org.openide.filesystems.EnvironmentNotSupportedException;
import org.openide.filesystems.FileUtil;
import org.openide.execution.NbProcessDescriptor;
import org.openide.execution.NbClassPath;
import org.openide.util.NbBundle;
/** Compiles (probably Java) sources via an external compiler process.
* The path to the executable, classpath, error-matching expressions, etc.
* are configurable.
*
* @author Ales Novak
*/
public class ExternalCompiler extends Compiler {
/** locales */
private static ResourceBundle bundle;
/** constant denoting Sun's compiler */
static final String SUN = getLocalizedString("CTL_Sun"); // NOI18N
/** constant denoting MS compiler */
static final String MICROSOFT = getLocalizedString("CTL_Microsoft"); // NOI18N
/** constant denoting Jikes compiler */
static final String IBM = getLocalizedString("CTL_Jikes"); // NOI18N
/** regular expression descripting Sun's compiler error format */
static final String SUN_ERROR_EXPR = "^([^ ][^\n]+):([0-9]+): (.*)"; // NOI18N
/** regular expression descripting MS compiler error format */
static final String MICROSOFT_ERROR_EXPR = "^([^\\(]+)\\(([0-9]+),([0-9]+)\\) : (.*)"; // NOI18N
/** regexp of JIKES */
static final String JIKES_ERROR_EXPR = "^([^ ]+):([0-9]+):([0-9]+):[0-9]+:[0-9]+:( |.*\n^)(.*)"; // NOI18N
/** Error parsing for Sun's Javac. */
public static final ErrorExpression JAVAC = new ErrorExpression(SUN, SUN_ERROR_EXPR, 1, 2, -1, 3);
/** Error parsing for Microsoft's JVC. */
public static final ErrorExpression JVC = new ErrorExpression(MICROSOFT, MICROSOFT_ERROR_EXPR, 1, 2, 3, 4);
/** Error parsing for IBM's Jikes (with the <code>+E</code> switch). */
public static final ErrorExpression JIKES = new ErrorExpression(IBM, JIKES_ERROR_EXPR, 1, 2, 3, 5);
/** Constant for compilation. */
public static final Object COMPILE = CompilerCookie.Compile.class;
/** Constant for building. */
public static final Object BUILD = CompilerCookie.Build.class;
/** Constant for cleaning. */
public static final Object CLEAN = CompilerCookie.Clean.class;
/** File extension for class files. */
protected static final String CLASS_EXTENSION = "class"; // NOI18N
/** can create the name of the file to compile */
private FNP fileNameProducer;
/** external compiler description - which file to exec, its args... */
private NbProcessDescriptor nbDescriptor;
/** ErrorExpression valid for this instance of external compiler */
private ErrorExpression errorExpression;
/** Type of the compilation. */
private Object type;
/** Create an external compiler.
* @param fo a file to compile
* @param type the type of compilation ({@link #COMPILE}, {@link #BUILD}, or {@link #CLEAN})
* @param nbDescriptor a description of an external compiler executable
* @param err a regular expression to scan for compiler errors
* @exception IllegalArgumentException if the file object is invalid
*/
public ExternalCompiler(
FileObject fo,
Object type,
NbProcessDescriptor nbDescriptor,
ErrorExpression err
) {
init (fo, type, nbDescriptor, err);
}
/** Create an external compiler.
* @param job the compiler job to add to
* @param fo a file to compile
* @param type the type of compilation ({@link #COMPILE}, {@link #BUILD}, or {@link #CLEAN})
* @param nbDescriptor a description of an external compiler executable
* @param err a regular expression to scan for compiler errors
* @exception IllegalArgumentException if the file object is invalid
*/
public ExternalCompiler(CompilerJob job, FileObject fo, Object type, NbProcessDescriptor nbDescriptor, ErrorExpression err) {
init (fo, type, nbDescriptor, err);
job.add (this);
registerInJob (job);
}
/** Create an external compiler with dependencies.
* @param dependencies an array of compilers that are to be invoked before this one.
* @param fo a file to compile
* @param type the type of compilation ({@link #COMPILE}, {@link #BUILD}, or {@link #CLEAN})
* @param nbDescriptor a description of an external compiler executable
* @param err a regular expression to scan for compiler errors
* @exception IllegalArgumentException if the file object is invalid
*/
public ExternalCompiler(Compiler[] dependencies, FileObject fo, Object type, NbProcessDescriptor nbDescriptor, ErrorExpression err) {
init (fo, type, nbDescriptor, err);
dependsOn (Arrays.asList (dependencies));
registerInJob (dependencies[0]);
}
/** Create an external compiler for a given java.io.File.
* @param file the file to compile
* @param type the type of compilation ({@link #COMPILE}, {@link #BUILD}, or {@link #CLEAN})
* @param nbDescriptor a description of an external compiler executable
* @param err a regular expression to scan for compiler errors
*/
public ExternalCompiler(
final java.io.File file,
Object type,
NbProcessDescriptor nbDescriptor,
ErrorExpression err
) {
if (
file == null ||
type == null ||
nbDescriptor == null ||
err == null
) {
throw new IllegalArgumentException();
}
this.fileNameProducer = new FNP () {
public String getFileName () {
return file.toString ();
}
public FileObject getFileObject () {
return null;
}
public boolean equalTo (FNP fnp) {
return fnp.equalToFile (file);
}
public boolean equalToFile (java.io.File f) {
return file.equals (f);
}
public boolean equalToResource (String res) {
return false;
}
public boolean equalToResourceAndFS (String res, FileSystem fs) {
return false;
}
public boolean equalToFileObject (FileObject obj) {
return false;
}
};
this.nbDescriptor = nbDescriptor;
this.errorExpression = err;
this.type = type;
}
/** Create an external compiler for an object in repository
* that still does not exists.
*
* @param fs file system to look on for resource
* @param resourceName the name of resource to look for
*
* @param type the type of compilation ({@link #COMPILE}, {@link #BUILD}, or {@link #CLEAN})
* @param nbDescriptor a description of an external compiler executable
* @param err a regular expression to scan for compiler errors
*/
public ExternalCompiler(
final FileSystem fs,
final String resourceName,
Object type,
NbProcessDescriptor nbDescriptor,
ErrorExpression err
) {
if (
fs == null ||
resourceName == null ||
type == null ||
nbDescriptor == null ||
err == null
) {
throw new IllegalArgumentException();
}
this.fileNameProducer = new FNP () {
public String getFileName () {
FileObject fo = fs.findResource (resourceName);
if (fo == null) {
return ""; // NOI18N
}
return NbClassPath.toFile (fo).toString ();
}
public FileObject getFileObject () {
return fs.findResource (resourceName);
}
public boolean equalTo (FNP fnp) {
return fnp.equalToResourceAndFS (resourceName, fs);
}
public boolean equalToFile (java.io.File f) {
return false;
}
public boolean equalToResource (String res) {
return false;
}
public boolean equalToResourceAndFS (String res, FileSystem fileSystem) {
return fs.equals (fileSystem) && res.equals (resourceName);
}
public boolean equalToFileObject (FileObject obj) {
return false;
}
};
this.nbDescriptor = nbDescriptor;
this.errorExpression = err;
this.type = type;
}
/** Create an external compiler for an object in repository
* need not exist in present. Looks on all filesystems.
*
* @param resourceName the name of resource to look for
*
* @param type the type of compilation ({@link #COMPILE}, {@link #BUILD}, or {@link #CLEAN})
* @param nbDescriptor a description of an external compiler executable
* @param err a regular expression to scan for compiler errors
*/
public ExternalCompiler(
final String resourceName,
Object type,
NbProcessDescriptor nbDescriptor,
ErrorExpression err
) {
if (
resourceName == null ||
type == null ||
nbDescriptor == null ||
err == null
) {
throw new IllegalArgumentException();
}
this.fileNameProducer = new FNP () {
public String getFileName () {
FileObject fo = FileSystemCapability.ALL.findResource (resourceName);
if (fo == null) {
return ""; // NOI18N
}
return NbClassPath.toFile (fo).toString ();
}
public FileObject getFileObject () {
return FileSystemCapability.ALL.findResource (resourceName);
}
public boolean equalTo (FNP fnp) {
return fnp.equalToResource (resourceName);
}
public boolean equalToFile (java.io.File f) {
return false;
}
public boolean equalToResource (String res) {
return res.equals (resourceName);
}
public boolean equalToResourceAndFS (String res, FileSystem fs) {
return false;
}
public boolean equalToFileObject (FileObject obj) {
return false;
}
};
this.nbDescriptor = nbDescriptor;
this.errorExpression = err;
this.type = type;
}
/** Initialization of basic parameters.
*/
private void init (
final FileObject fo,
Object type,
NbProcessDescriptor nbDescriptor,
ErrorExpression err
) {
if (
fo == null ||
type == null ||
nbDescriptor == null ||
err == null) {
throw new IllegalArgumentException();
}
this.fileNameProducer = new FNP () {
public String getFileName () {
return org.openide.execution.NbClassPath.toFile (fo).toString ();
}
public FileObject getFileObject () {
return fo;
}
public boolean equalTo (FNP fnp) {
return fnp.equalToFileObject (fo);
}
public boolean equalToFile (java.io.File f) {
return false;
}
public boolean equalToResource (String res) {
return false;
}
public boolean equalToResourceAndFS (String res, FileSystem fs) {
return false;
}
public boolean equalToFileObject (FileObject obj) {
return obj.equals (fo);
}
};
this.nbDescriptor = nbDescriptor;
this.errorExpression = err;
this.type = type;
}
/** Get the description of the compiler executable.
* @return the descriptor */
public final NbProcessDescriptor getCompilerDescriptor() {
return nbDescriptor;
}
/** Get the error expression used to parse error output from this compiler.
* @return the error expression
*/
public final ErrorExpression getErrorExpression() {
return errorExpression;
}
/** Get the file object to be compiled
* @return the file object (can be null if constructor without
* file object argument has been used and the file object does not exists)
*/
protected final FileObject getFileObject() {
return fileNameProducer.getFileObject ();
}
/** Get the name of the file to be compiled.
* @return the file name
*/
public String getFileName() {
return fileNameProducer.getFileName ();
}
/** @retrun <tt>true</tt> iff the fo is up to date */
private static boolean isUpToDate(FileObject fo) {
FileObject clsfo;
// find all entries if the fo belongs to a MultiDataObject
try {
org.openide.loaders.DataObject dataobj = org.openide.loaders.DataObject.find(fo);
java.util.Set files = dataobj.files();
java.util.Iterator iter = files.iterator();
int classfilescount = 0;
while (iter.hasNext()) {
clsfo = (FileObject) iter.next();
if (clsfo.getExt().equals(CLASS_EXTENSION)) {
classfilescount++;
if (clsfo.lastModified().compareTo(fo.lastModified ()) < 0) {
// class fo does not exists or is older then the java source
// => compile the file
return false;
}
}
}
if (classfilescount == 0) {
return false;
} else {
return true;
}
} catch (org.openide.loaders.DataObjectNotFoundException e) {
return false;
}
}
/* inherited */
public boolean isUpToDate() {
// if (type == CLEAN) { error!!! }
if (type == BUILD) {
return false;
} else {
FileObject fo = getFileObject ();
return fo == null || isUpToDate(fo);
}
}
public String toString() {
StringBuffer sb = new StringBuffer(getClass().getName());
sb.append(" for "); // NOI18N
sb.append(getFileName());
sb.append(" "); // NOI18N
if (type == COMPILE) sb.append("COMPILE"); // NOI18N
if (type == BUILD) sb.append("BUILD"); // NOI18N
if (type == CLEAN) sb.append("CLEAN"); // NOI18N
return sb.toString();
}
/** @return ExternalCompilerGroup
*/
public Class compilerGroupClass() {
return ExternalCompilerGroup.class;
}
/** Identifier for type of compiler. This method allows subclasses to specify
* the type this compiler belongs to. Compilers that belong to the same class
* will be compiled together by one external process.
* <P>
* It is necessary for all compilers of the same type to have same process
* descriptor and error expression.
* <P>
* This implementation returns the process descriptor, so all compilers
* with the same descriptor will be compiled at once.
* <p><em>Note</em> that this method has no relation to {@link CompilerType}s; the name is incidental.
* @return key to define type of the compiler
* @see ExternalCompilerGroup#createProcess
* @deprecated While subclassing this method and specifying a type will still work,
* it is no longer recommended. Instead, please use {@link Compiler#compilerGroupKey}
* and make all compiler-specific state available to the compiler group via other means
* (such as getter methods).
*/
protected Object compilerType () {
return nbDescriptor;
}
/** Produce a refined key for external compilers.
* Groups not only by the compiler group class, but by the result of {@link #compilerType} as well,
* and also by the error description.
* Specialized external compilers are encouraged to override this method directly according to the
* contract specified in {@link Compiler#compilerGroupKey}.
* @return a composite key
*/
public Object compilerGroupKey () {
List l = new ArrayList (3);
l.add (super.compilerGroupKey ());
l.add (compilerType ());
l.add (errorExpression);
return l;
}
/** Two external compilers are the same if they have been constructed with
* the same arguments. If one has been constructed with the
* java.io.File argument and second with resource name then they are different.
*
*/
public boolean equals (Object o) {
if (o instanceof ExternalCompiler) {
ExternalCompiler c = (ExternalCompiler)o;
return
fileNameProducer.equalTo (c.fileNameProducer) &&
compilerGroupKey ().equals (c.compilerGroupKey ());
}
return false;
}
public int hashCode() {
String fn = getFileName();
return (fn == null) ? 0 : fn.hashCode();
}
/** @return localized String */
static String getLocalizedString(String s) {
if (bundle == null) {
bundle = NbBundle.getBundle(ExternalCompiler.class);
}
return bundle.getString(s);
}
/** Encapsulates several properties needed for processing
* the error output of an external compiler.
*/
public static class ErrorExpression implements java.io.Serializable, Cloneable {
/** name of the compiler */
private String name;
/** a regular expression */
private String errordesc;
/** position of the file with error inside the expression */
private int filepos;
/** position of the line */
private int linepos;
/** positiom of the column */
private int columnpos;
/** position of the description */
private int descpos;
static final long serialVersionUID =-2647801563993403964L;
/** Create an error expression.
* @param name display name
* @param errordesc Perl5-style regular expression which should match lines containing error output
* @param filepos index of backreference containing the filename of the error, or <code>-1</code> if none
* @param linepos index of backreference containing the line number of the error, or <code>-1</code> if none
* @param columnpos index of backreference containing the column number of the error, or <code>-1</code> if none
* @param descpos index of backreference containing the description of the error, or <code>-1</code> if none
*/
public ErrorExpression (String name, String errordesc, int filepos, int linepos, int columnpos, int descpos) {
this.name = name;
this.errordesc = errordesc;
this.filepos = filepos;
this.linepos = linepos;
this.columnpos = columnpos;
this.descpos = descpos;
}
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
// cannot happen
throw new InternalError ();
}
}
/** Get the display name.
* @return the display name
*/
public String getName () {
return name;
}
/** Set the display name.
* @param d the new name
*/
public void setName(String d) {
name = d;
}
/** Get the error regexp.
* @return the error regexp
*/
public String getErrorExpression () {
return errordesc;
}
/** Set the error regexp.
* @param d the new regexp
*/
public void setErrorExpression(String s) {
errordesc = s;
}
/** Get the filename backreference.
* @return the index, or <code>-1</code> for none
*/
public int getFilePos () {
return filepos;
}
/** Set the filename backreference.
* @param d the new index (<code>-1</code> to disable)
*/
public void setFilePos(int i) {
filepos = i;
}
/** Get the line-number backreference.
* @return the index, or <code>-1</code> for none
*/
public int getLinePos () {
return linepos;
}
/** Set the line-number backreference.
* @param d the new index (<code>-1</code> to disable)
*/
public void setLinePos(int i) {
linepos = i;
}
/** Get the column-number backreference.
* @return the index, or <code>-1</code> for none
*/
public int getColumnPos () {
return columnpos;
}
/** Set the column-number backreference.
* @param d the new index (<code>-1</code> to disable)
*/
public void setColumnPos(int i) {
columnpos = i;
}
/** Get the description backreference.
* @return the index, or <code>-1</code> for none
*/
public int getDescriptionPos () {
return descpos;
}
/** Set the description backreference.
* @param d the new index (<code>-1</code> to disable)
*/
public void setDescriptionPos(int i) {
descpos = i;
}
public boolean equals (Object o) {
if ((o == null) || (!(o instanceof ErrorExpression))) return false;
ErrorExpression him = (ErrorExpression) o;
return name.equals(him.name) &&
errordesc.equals(him.errordesc) &&
filepos == him.filepos &&
linepos == him.linepos &&
columnpos == him.columnpos &&
descpos == him.descpos;
}
public int hashCode () {
return name.hashCode();
}
}
/** Internal interface to provide different methods for locating of
* file object and file name.
*/
interface FNP {
public String getFileName ();
public FileObject getFileObject ();
/** JST: Do you know how SmallTalk compares numbers? Let's try
* the same comparing here....
*
* So three different subclasses => four comparing methods
*
* But as usual there is a room for improvement.
*/
public boolean equalTo (FNP fnp);
public boolean equalToFile (java.io.File f);
public boolean equalToResource (String res);
public boolean equalToResourceAndFS (String res, FileSystem fs);
public boolean equalToFileObject (FileObject obj);
}
}
/*
* Log
* 24 src-jtulach1.23 2/7/00 Ales Novak #5656
* 23 src-jtulach1.22 1/18/00 Jaroslav Tulach External Compiler is
* initialized first and than its dependencies are handled (caused
* problems in the hashCode) method.
* 22 src-jtulach1.21 1/15/00 Petr Jiricka hashCode() added.
* 21 src-jtulach1.20 1/14/00 Petr Jiricka toString() method added.
* 20 src-jtulach1.19 1/12/00 Ian Formanek NOI18N
* 19 src-jtulach1.18 12/23/99 Jaroslav Tulach Enhancing compiler API
* to makefile capabilities
* 18 src-jtulach1.17 10/22/99 Ian Formanek NO SEMANTIC CHANGE - Sun
* Microsystems Copyright in File Comment
* 17 src-jtulach1.16 10/5/99 Ales Novak isUpToDate method bugfix
* 16 src-jtulach1.15 10/1/99 Jaroslav Tulach FileObject.move &
* FileObject.copy
* 15 src-jtulach1.14 9/29/99 Ales Novak isUpToDate does not
* delete files if CLEAN is specified
* 14 src-jtulach1.13 9/10/99 Jesse Glick Small API change:
* ExternalCompiler.compilerType -> Compiler.compilerGroupKey.
* 13 src-jtulach1.12 8/9/99 Ian Formanek Generated Serial Version
* UID
* 12 src-jtulach1.11 8/5/99 Ales Novak BUILD actions remove
* possibly generated classes before compilation
* 11 src-jtulach1.10 6/8/99 Ian Formanek ---- Package Change To
* org.openide ----
* 10 src-jtulach1.9 6/2/99 Jaroslav Tulach ExternalCompiler has
* method for specifying its type.
* 9 src-jtulach1.8 4/21/99 Jesse Glick [JavaDoc]
* 8 src-jtulach1.7 4/16/99 Martin Ryzl getFileObject() added
* 7 src-jtulach1.6 4/2/99 Jesse Glick [JavaDoc]
* 6 src-jtulach1.5 4/2/99 Ales Novak
* 5 src-jtulach1.4 4/1/99 Ales Novak
* 4 src-jtulach1.3 3/29/99 Jesse Glick [JavaDoc]
* 3 src-jtulach1.2 3/29/99 Jaroslav Tulach ErrorExpression.clone
* does not throw error
* 2 src-jtulach1.1 3/28/99 Ales Novak
* 1 src-jtulach1.0 3/28/99 Ales Novak
* $
*/